package tmt.usercenter.web.service;

import com.tmt.annotation.ResourceIdDescriptor;
import com.tmt.annotation.ScopeDescriptor;
import com.tmt.annotation.parser.EnumAnnotationParser;
import com.tmt.helper.PackageHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tmt.usercenter.web.Application;
import tmt.usercenter.web.configure.bean.UserAuditorAware;
import tmt.usercenter.web.domain.*;
import tmt.usercenter.web.domain.enums.*;

import java.util.*;

/**
 * 用于初始化数据表中的初始数据。
 * 本类中调用getRepo()方法进行实体的创建和更新，
 * 而没有调用对应的create、update方法，主要是为了绕过授权验证。
 */
@Service
@Transactional
public class TableDataInitialService {

	@Autowired
	private TmtUserService userService;

	@Autowired
	private ClientSystemService clientService;
	@Autowired
	private PasswordEncoder passwordEncoder;

	@Autowired
	private TmtAuthorityService authorityService;

	@Autowired
	private TmtRoleService roleService;

	@Autowired
	private TmtUserRoleService userRoleService;

	@Autowired
	private SystemParamService systemParamService;

	@Autowired
	private AuthScopeService authScopeService;

	@Autowired
	private AuthResourceService authResourceService;

	/**
	 * 初始化相关数据表
	 */
	public synchronized void initialTables() {
		/**
		 * 本方法中调用getRepo()方法进行实体的创建和更新，
		 * 而没有调用对应的create、update方法，主要是为了绕过授权验证。
		 */
		try {
			initialSystemParam();

			TmtUser sysUser = initialSysAdminUser();
			UserAuditorAware.currentUser = sysUser;

			//创建本应用的客户系统记录。
			ClientSystem cs = initialClientSystem(sysUser);

			initialAuthScope(cs);

			//初始化权限
			initialAuthority(cs);

			//初始化角色
			initialRole(cs);

			updateRolesForSysAdmin();

		} catch (Exception ex) {
			ex.printStackTrace();
		} finally {
			UserAuditorAware.currentUser = null;
		}
	}

	private TmtUser initialSysAdminUser() {
		TmtUser user = userService.findByUsername("sysadmin");
		if (user == null) {
			user = new TmtUser();
			user.setUsername("sysadmin");
			user.setAvatar("");
			user.setEmail("11372349@qq.com");
			user.setEnabled(true);
			user.setPassword(passwordEncoder.encode("123456"));
			user.setNickname("sysadmin");
			user.setRealName("sysadmin");
			user.setMobile("1801801800");
			user.setUserType(TmtUserType.SYSADMIN);
			user.setEmailEnabled(true);
			user.setCardType(IdCardType.ID_CARD_2);
			user.setIdCardNum("511122197509298812");
			user.setOnline(false);
			user.setMobileEnabled(false);
			user.setLoginCount(0);
			user = userService.getRepo().save(user);
		}
		return user;
	}

	private ClientSystem initialClientSystem(TmtUser sysUser) {
		ClientSystem cs = clientService.findByClientId(Application.SYSTEM_ID);
		if (cs == null) {
			cs = new ClientSystem();
			cs.setAccessTokenValiditySeconds(10);
			cs.setAuthorizedGrantTypes(Arrays.asList(AuthorizedGrantType.values()));
			cs.setAutoApprove(false);
			cs.setChangedNotifyUrl("/");
			cs.setClientId(Application.SYSTEM_ID);
			cs.setClientSecret("123456");
			cs.setEmail("11372349@qq.com");
			cs.setLogo("");
			cs.setName("TMT用户中心");
			cs.setRefreshTokenValiditySeconds(10);
			cs.setRegisteredRedirectUri(Arrays.asList("/personal/view"));
			cs.setLogoutNotifyUrl("/notify/logout");
			cs.setCreator(sysUser);
			cs = clientService.getRepo().save(cs);
			sysUser.setOriginSystem(cs);
			userService.getRepo().save(sysUser);
		}
		return cs;
	}

	private void initialAuthority(ClientSystem cs) {
		List<TmtAuthority> authorities = new ArrayList<>();
		List<AuthorityItem> list = Arrays.asList(AuthorityItem.values());
		for (AuthorityItem x : list) {
			TmtAuthority authority = new TmtAuthority();
			String name = EnumAnnotationParser.getTitle(x);
			String description = EnumAnnotationParser.getDescription(x);
			authority.setName(name);
			authority.setDescription(description);
			authority.setAuthority(x.name());
			authority.setClientTag(x.getClientTag());
			authority.setAccessPoint(Arrays.asList(x.getAccessPoint()));
			authority.setSort(x.getSort());
			authority.setAuthorityType(x.getAuthorityType());
			authority.setOperationType(x.getOperationType());
			authority.setParentCode(x.getParent());
			authority.setOriginSystem(cs);
			authorities.add(authority);
		}
		authorityService.updateAuthorities(authorities);
	}

	private void initialRole(ClientSystem cs) {
		List<TmtRole> roles = new ArrayList<>();
		List<RoleItem> list = Arrays.asList(RoleItem.values());
		for (RoleItem x : list) {
			TmtRole role = new TmtRole();
			roles.add(role);

			String name = EnumAnnotationParser.getTitle(x);
			String description = EnumAnnotationParser.getDescription(x);
			role.setName(name);
			role.setRemark(description);
			role.setCode(x.name());
			role.setDefaultRole(x.isDefaultRole());
			role.setOriginSystem(cs);

			List<TmtRoleAuthority> roleAuthorities = new ArrayList<>();
			role.setRoleAuthorities(roleAuthorities);
			Arrays.asList(x.getAuthorities()).forEach(y -> {
				TmtRoleAuthority roleAuthority = new TmtRoleAuthority();
				roleAuthorities.add(roleAuthority);
				TmtAuthority authority = new TmtAuthority();
				roleAuthority.setAuthority(authority);
				authority.setAuthority(y.name());
			});
		}
		roleService.updateRoles(roles);
	}

	private void updateRolesForSysAdmin() {
		TmtUser admin = userService.findByUsername("sysadmin");
		List<RoleItem> roles = Arrays.asList(RoleItem.SUPER_ADMIN);

		List<TmtUserRole> userRoles = new ArrayList<>();
		roles.forEach(x -> {
			TmtRole role = roleService.findByCode(x.name());
			if (!userRoleService.existsByUserAndRole(admin, role)) {
				TmtUserRole userRole = new TmtUserRole();
				userRole.setUser(admin);
				userRole.setRole(role);
				userRoles.add(userRole);
			}
		});
		userRoleService.getRepo().save(userRoles);
	}

	private void initialSystemParam() {
		List<SystemParam> systemParams = new ArrayList<>();
		Arrays.stream(SystemParamCode.values()).forEach(x -> {
			SystemParam sp = new SystemParam();
			sp.setParamCode(x.name());
			sp.setParamName(EnumAnnotationParser.getTitle(x));
			sp.setParamNote(EnumAnnotationParser.getDescription(x));
			sp.setParamValue(x.getParamValue());
			systemParams.add(sp);
		});

		List<SystemParam> currentAllParams = systemParamService.findAll(SystemParam.class, null);

		//删除不存在的参数。
		List<SystemParam> deletedParams = new ArrayList<>();
		currentAllParams.forEach(x -> {
			if (systemParams.stream().noneMatch(y -> Objects.equals(y.getParamCode(), x.getParamCode())))
				deletedParams.add(x);
		});
		systemParamService.getRepo().delete(deletedParams);
		currentAllParams.removeAll(deletedParams);

		systemParams.forEach(x -> {
			Optional<SystemParam> optional = currentAllParams.stream().filter(y -> Objects.equals(y.getParamCode(), x.getParamCode())).findFirst();
			if (optional.isPresent()) {
				SystemParam sp = optional.get();
				sp.setParamName(x.getParamName());
				systemParamService.getRepo().save(sp);
			} else {
				systemParamService.getRepo().save(x);
			}
		});
	}

	private void initialAuthScope(ClientSystem cs) {

		//搜索当前包中所有的@ScopeDescriptor对象。
		List<AuthScope> curAuthScopes = authScopeService.findAllBySystemScope(true);

		List<AuthScope> authScopes = new ArrayList<>();
		List<ScopeDescriptor> descriptors = PackageHelper.findAnnotionInstance(Application.class.getPackage().getName(), ScopeDescriptor.class);
		descriptors.stream()
				.parallel().forEach(x -> {
			if (curAuthScopes.stream().noneMatch(y-> Objects.equals(y.getScope(), x.value()))) {
				AuthScope scope = new AuthScope(x.title(), x.value(), x.description(), x.order(), true, cs, new ArrayList<>(), new ArrayList<>());
				authScopes.add(scope);
			}
		});
		authScopeService.getRepo().save(authScopes);

		//搜索当前包中所有的@ScopeDescriptor对象。
		List<AuthResource> curAuthResources = authResourceService.findAllBySystemResource(true);

		List<AuthResource> authResources = new ArrayList<>();
		Map<Object, ResourceIdDescriptor> riDescriptors = PackageHelper.findAnnotionInstance(Application.class.getPackage().getName(),
				EnableResourceServer.class, ResourceIdDescriptor.class);
		riDescriptors.entrySet().stream()
				.parallel().forEach(x -> {
			Optional<AuthScope> optional = authScopes.stream().filter(y-> Objects.equals(y.getScope(), x.getValue().scope()[0])).findFirst();
			if (optional.isPresent()) {
				if (curAuthResources.stream().noneMatch(y-> Objects.equals(y.getResourceId(), x.getKey()))) {
					AuthScope authScope = optional.get();
					AuthResource resource = new AuthResource(x.getValue().title(), (String) x.getKey(),
							x.getValue().description(), x.getValue().order(), true, authScope, cs, new ArrayList<>());
					authResources.add(resource);
				}
			}
		});
		authResourceService.getRepo().save(authResources);
	}

}
